#include <regex>
#include <iostream>
#include "config.hpp"
#include "util.hpp"
#include "game.hpp"
#include "displayer.hpp"
#include "display_str.hpp"

Game::Game(void)
{
    ResetGame();
}

Game::~Game(void)
{
}

bool Game::Ready(void)
{
    return m_user_num > 0 && !m_gameover;
}

void Game::ResetGame(void)
{
    for (size_t i = 0; i < MAX_USER_NUM; ++i)
        m_actor[i].InitActor(this);
    InitLand();
    m_name2idx.clear();
    m_user_num = 0;
    m_user_order = "";
    m_current_user = 0;
    m_input_tip = "";
    m_die_cnt = 0;
    m_tip_idx = 0;
    m_tip_clear = false;
    m_tip_queue.clear();
    m_tip_queue.resize(MAX_TIP_LINE);
    m_gameover = false;
}

void Game::SetDefaultFund(int f)
{
    m_default_fund = f;
}

void Game::InitLand(void)
{
    m_land[0].Init(LandTypeStart);
    for (size_t i = 1; i < 14; ++i)
        m_land[i].Init(LandTypeEmpty1);
    m_land[14].Init(LandTypePark);
    for (size_t i = 15; i < 28; ++i)
        m_land[i].Init(LandTypeEmpty1);
    m_land[28].Init(LandTypeTool);
    for (size_t i = 29; i < 35; ++i)
        m_land[i].Init(LandTypeEmpty2);
    m_land[35].Init(LandTypeGift);
    for (size_t i = 36; i < 49; ++i)
        m_land[i].Init(LandTypeEmpty3);
    m_land[49].Init(LandTypePark);
    for (size_t i = 50; i < 63; ++i)
        m_land[i].Init(LandTypeEmpty3);
    m_land[63].Init(LandTypeMagic);
    for (size_t i = 64; i < 70; ++i)
        m_land[i].Init(LandTypeMine);
}

bool Game::SetUser(std::string users)
{
    users = to_upper(users);
    auto len = users.length();
    if (len < 2 || len > 4)
        return false;
    for (size_t i = 0; i < len; ++i)
    {
        if (!isPlayerName(users[i]))
            return false;
        if (users.find(users[i], i + 1) != users.npos)
            return false;
    }
    ResetGame();
    m_user_num = len;
    m_user_order = users;
    for (size_t i = 0; i < m_user_num; ++i)
    {
        m_name2idx[users[i]] = i;
        m_actor[i].m_name = users[i];
    }
    return true;
}

std::string Game::GetUser(void)
{
    return m_user_order;
}

bool Game::SetCurrentUser(char cur_user)
{
    cur_user = toupper(cur_user);
    if (m_name2idx.find(cur_user) != m_name2idx.end() && !m_actor[m_name2idx[cur_user]].m_bankruptcy)
    {
        m_current_user = m_name2idx[cur_user];
        return true;
    }
    return false;
}

int Game::GetCurrentUser(void)
{
    return m_current_user;
}

bool Game::SetUserLocation(char user, int loc, int delay)
{
    user = toupper(user);
    if (m_name2idx.find(user) == m_name2idx.end())
        return false;
    Actor &actor = m_actor[m_name2idx[user]];
    if (loc < 0 || loc >= 70)
        return false;
    if (delay < 0)
        return false;
    if (m_land[loc].barrier)
        return false;
    actor.m_inMagic = delay;
    if (actor.m_loc == 0 && loc != 0)
        actor.m_start = true;
    actor.m_loc = loc;
    return true;
}

bool Game::SetLandOwner(char user, int loc, int level)
{
    user = toupper(user);
    if (loc < 0 || loc >= 70 || level < 0 || level > 4)
        return false;
    if (!isPlayerName(user))
        return false;
    if (m_name2idx.find(user) == m_name2idx.end())
        return false;
    Land &land = m_land[loc];
    if (!(land.type == LandTypeEmpty1 || land.type == LandTypeEmpty2 || land.type == LandTypeEmpty3))
        return false;
    if (land.owner != '\0' && land.owner != user)
        return false;
    if (level < land.level)
        return false;
    land.owner = user;
    land.level = level;
    return true;
}

bool Game::SetFund(char user, int num)
{
    user = toupper(user);
    if (!isPlayerName(user))
        return false;
    if (m_name2idx.find(user) == m_name2idx.end())
        return false;
    if (num < 0)
        return false;
    m_actor[m_name2idx[user]].m_fund = num;
    return true;
}

bool Game::SetCredit(char user, int num)
{
    user = toupper(user);
    if (!isPlayerName(user))
        return false;
    if (m_name2idx.find(user) == m_name2idx.end())
        return false;
    if (num < 0)
        return false;
    m_actor[m_name2idx[user]].m_credit = num;
    return true;
}

bool Game::SetBarrierNum(char user, int num)
{
    user = toupper(user);
    if (!isPlayerName(user))
        return false;
    if (m_name2idx.find(user) == m_name2idx.end())
        return false;
    Actor &actor = m_actor[m_name2idx[user]];
    if (num < 0 || num + actor.m_robot > 10)
        return false;
    actor.m_barrier = num;
    return true;
}
bool Game::SetRobotNum(char user, int num)
{
    user = toupper(user);
    if (!isPlayerName(user))
        return false;
    if (m_name2idx.find(user) == m_name2idx.end())
        return false;
    Actor &actor = m_actor[m_name2idx[user]];
    if (num < 0 || actor.m_barrier + num > 10)
        return false;
    actor.m_robot = num;
    return true;
}
bool Game::SetGodNum(char user, int num)
{
    user = toupper(user);
    if (!isPlayerName(user))
        return false;
    if (m_name2idx.find(user) == m_name2idx.end())
        return false;
    Actor &actor = m_actor[m_name2idx[user]];
    if (num < 0)
        return false;
    actor.m_god = num;
    return true;
}

bool Game::PlaceBarrier(int loc)
{
    if (loc < 0 || loc >= 70)
        return false;
    Land &land = m_land[loc];
    if (land.barrier)
        return false;
    if (land.type != LandTypeEmpty1 && land.type != LandTypeEmpty2 && land.type != LandTypeEmpty3)
        return false;
    for (size_t i = 0; i < m_user_num; ++i)
        if (m_actor[i].m_loc == loc)
            return false;
    land.barrier = true;
    return true;
}
bool Game::PlaceRobot(int loc)
{
    if (loc < 0 || loc >= 70)
        return false;
    for (int i = 1; i <= ROBOT_STEP; ++i)
        m_land[(loc + i) % 70].barrier = false;
    return true;
}

bool Game::SellLand(char user, int loc)
{
    user = toupper(user);
    if (!isPlayerName(user))
        return false;
    if (m_name2idx.find(user) == m_name2idx.end())
        return false;
    if (loc < 0 || loc >= 70)
        return false;
    Land &land = m_land[loc];
    if (land.owner != user)
        return false;
    land.owner = '\0';
    m_actor[m_name2idx[user]].GetMoney(2 * land.GetLandPrice());
    land.level = 0;
    return true;
}

int Game::ActorStep(Actor &actor)
{
    if (actor.m_inMagic > 0)
    {
        //需要付钱
        Land &land = m_land[actor.m_loc];
        switch (land.type)
        {
        case LandTypeEmpty1:
        case LandTypeEmpty2:
        case LandTypeEmpty3:
            if (land.owner != '\0' && actor.m_name != land.owner)
            {
                if (actor.m_god <= 0)
                {
                    if (m_name2idx.find(land.owner) != m_name2idx.end())
                    {
                        auto land_owner = m_name2idx[land.owner];
                        auto res = actor.PayRent(land);
                        if (res <= 0)
                            return 1;
                        m_actor[land_owner].GetMoney(res);
                    }
                }
                else
                    SetTip(GodOnBodyStr);
            }
            break;
        default:
            break;
        }
        --actor.m_inMagic;
        if (actor.m_god > 0)
            --actor.m_god;
        return 0;
    }
    actor.m_sell = false;
    int step = actor.UserAction();
    if (step < 0)
        return step;
    actor.m_start = true;

    for (int i = 0; i < step; ++i)
    {
        actor.m_loc = actor.m_loc >= 69 ? 0 : actor.m_loc + 1;
        if (m_land[actor.m_loc].barrier)
        {
            m_land[actor.m_loc].barrier = false;
            break;
        }
    }
    UpdateDisplay();
    Land &land = m_land[actor.m_loc];
    switch (land.type)
    {
    case LandTypeEmpty1:
    case LandTypeEmpty2:
    case LandTypeEmpty3:
        if (actor.m_name == land.owner)
        {
            if (land.level < 3 && actor.QueryUpdateBuliding(land))
                ++land.level;
        }
        else if (land.owner == 0)
        {
            if (actor.QueryBuyEmpty(land))
                land.owner = actor.m_name;
        }
        else
        {
            if (actor.m_god <= 0)
            {
                auto land_owner = m_name2idx[land.owner];
                auto res = actor.PayRent(land);
                if (res <= 0)
                    return 1;
                m_actor[land_owner].GetMoney(res);
            }
            else
                SetTip(GodOnBodyStr);
        }
        break;
    case LandTypeTool:
        actor.ToolHouse();
        break;
    case LandTypeGift:
        actor.GiftHouse();
        break;
    case LandTypeMagic:
    {
        int player_id = actor.MagicHouse();
        if (player_id >= 0)
            m_actor[player_id].Enchanted();
        break;
    }
    case LandTypeMine:
        actor.Mine(actor.m_loc);
        break;
    case LandTypePark:
        break;
    default:
        break;
    }
    if (actor.m_god > 0)
        --actor.m_god;
    return 0;
}

bool Game::Step(void)
{
    Actor &actor = m_actor[m_current_user];
    if (actor.m_bankruptcy)
        return true;
    auto res = ActorStep(actor);
    if (res == -2)
        return true; //TODO
    m_current_user = m_current_user == (int)m_user_num - 1 ? 0 : m_current_user + 1;
    while (m_actor[m_current_user].m_bankruptcy)
        m_current_user = m_current_user == (int)m_user_num - 1 ? 0 : m_current_user + 1;
    if (res == -1)
        return true;
    if (res == 1)
    {
        for (auto &land : m_land)
            if (land.owner == actor.m_name)
            {
                land.owner = 0;
                land.level = 0;
            }
        ++m_die_cnt;
        if (m_die_cnt == (int)m_user_num - 1)
        {
            for (size_t i = 0; i < m_user_num; ++i)
                if (!m_actor[i].m_bankruptcy)
                {
                    SetTip(GameOverStr + GetCName(m_actor[i].m_name) + WinStr);
                    break;
                }
            m_gameover = true;
            return false;
        }
    }
    return true;
}

void Game::SetDisplayer(Displayer *dis)
{
    m_display = dis;
}

void Game::UpdateDisplay(void)
{
    m_display->Update();
}

void Game::ClearInputTip(void)
{
    m_input_tip = "";
    UpdateDisplay();
}

void Game::FullScreenTip(std::string s)
{
    m_tip_idx = 0;
    m_tip_queue.clear();
    m_tip_queue.resize(MAX_TIP_LINE);
    m_tip_queue[0] = s;
    m_tip_clear = true;
    UpdateDisplay();
}

void Game::SetTip(std::string s)
{
    if (m_tip_clear)
    {
        m_tip_idx = 0;
        m_tip_queue.clear();
        m_tip_queue.resize(MAX_TIP_LINE);
    }
    m_tip_queue[m_tip_idx] = s;
    m_tip_idx = m_tip_idx < MAX_TIP_LINE - 1 ? m_tip_idx + 1 : 0;
    UpdateDisplay();
}

void Game::SetInputTip(std::string s)
{
    m_input_tip = s;
    UpdateDisplay();
}